package cn.monkey.family.data.sys.address;

import cn.monkey.family.data.DataMapper;
import cn.monkey.family.data.KVPair;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ReportingPolicy;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
public interface AddressMapper extends DataMapper<AddressDto, Address, AddressVo> {

    @Override
    @Mapping(target = "province", expression = "java(cn.monkey.family.data.KVPair.of(address.getProvinceCode(),address.getProvinceName()))")
    @Mapping(target = "city", expression = "java(cn.monkey.family.data.KVPair.of(address.getCityCode(),address.getCityName()))")
    @Mapping(target = "country", expression = "java(cn.monkey.family.data.KVPair.of(address.getCountryCode(),address.getCountryName()))")
    @Mapping(target = "street", expression = "java(cn.monkey.family.data.KVPair.of(address.getStreetCode(),address.getStreetName()))")
    AddressVo toVo(Address address);

    @Override
    @Mapping(target = "provinceCode", source = "province.k")
    @Mapping(target = "provinceName", source = "province.v")
    @Mapping(target = "cityCode", source = "city.k")
    @Mapping(target = "cityName", source = "city.v")
    @Mapping(target = "countryCode", source = "country.k")
    @Mapping(target = "countryName", source = "country.v")
    @Mapping(target = "streetCode", source = "street.k")
    @Mapping(target = "streetName", source = "street.v")
    Address toEntity(AddressDto addressDto);

    default AddressTree from(KVPair<String, String> data) {
        AddressTree addressTree = new AddressTree();
        addressTree.setData(data);
        return addressTree;
    }

    default Collection<AddressTree> toTree(Collection<AddressVo> addressVos) {
        if (CollectionUtils.isEmpty(addressVos)) {
            return Collections.emptyList();
        }
        List<Function<AddressVo, AddressTree>> functions = List.of(vo -> this.from(vo.getProvince()),
                vo -> this.from(vo.getCity()),
                vo -> this.from(vo.getCountry()),
                vo -> this.from(vo.getStreet()));

        List<AddressTree> root = null;
        List<AddressTree> parentNode = null;
        Map<AddressTree, List<AddressVo>> treeListMap = null;
        for (Function<AddressVo, AddressTree> function : functions) {
            if (root == null) {
                root = parentNode = addressVos.stream().map(function).toList();
                // 获取子节点
                treeListMap = addressVos.stream().collect(Collectors.groupingBy(function));
                continue;
            }
            parentNode = this.addChild(parentNode, treeListMap, function);
            treeListMap = addressVos.stream().collect(Collectors.groupingBy(function));
        }
        return root;
    }

    default List<AddressTree> addChild(List<AddressTree> addressVoList,
                                       Map<AddressTree, List<AddressVo>> map,
                                       Function<AddressVo, AddressTree> function) {
        List<AddressTree> allChild = new ArrayList<>();
        for (AddressTree addressVo : addressVoList) {
            List<AddressVo> addresses = map.get(addressVo);
            if (CollectionUtils.isEmpty(addresses)) {
                continue;
            }
            List<AddressTree> child = addresses.stream().map(function)
                    .distinct().toList();
            addressVo.setChild(child);
            allChild.addAll(child);
        }
        return allChild;
    }

    default String[] toStrLine(AddressVo addressVo) {
        String[] ss = new String[8];
        ss[0] = addressVo.getProvince().getK();
        ss[1] = addressVo.getProvince().getV();
        ss[2] = addressVo.getCity().getK();
        ss[3] = addressVo.getCity().getV();
        ss[4] = addressVo.getCountry().getK();
        ss[5] = addressVo.getCountry().getV();
        ss[6] = addressVo.getStreet().getK();
        ss[7] = addressVo.getStreet().getV();
        return ss;
    }
}
